﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Birkelund.QueryStrings.Tokens;
using NUnit.Framework;

namespace Birkelund.QueryStrings.Test
{
    public class UriWriterTests
    {
        #region Static Initializers
        [Test]
        public void StaticInitializer_EmptyFromUrl()
        {
            //arrange
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?id=123");
            string expected = new Uri(@"http://localhost/test/testFile.aspx").ToString();

            //act
            IUriWriter writer = UriWriter.CreateEmptyFromUri(initialUrl);

            //assert
            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_FromUrl()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?id=123");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?id=123").ToString();

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected.ToString(), writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_FromUrl_NoFile()
        {
            Uri initialUrl = new Uri(@"http://localhost:58145/");
            string expected = new Uri(@"http://localhost:58145/").ToString();

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected.ToString(), writer.GenerateQueryAsString());
        }        

        [Test]
        public void StaticInitializer_FromString()
        {
            string initialUrl = @"http://localhost/test/testFile.aspx?id=123";
            string expected = @"http://localhost/test/testFile.aspx?id=123";

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected.ToString(), writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_EmptyFromString()
        {
            string initialUrl = @"http://localhost/test/testFile.aspx?id=123";
            string expected = @"http://localhost/test/testFile.aspx";

            IUriWriter writer = UriWriter.CreateEmptyFromUri(initialUrl);
            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_FromUrl_SSL()
        {
            Uri initialUrl = new Uri(@"https://localhost/test/testFile.aspx?id=123");
            string expected = new Uri(@"https://localhost/test/testFile.aspx?id=123").ToString();

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_EmptyFromUrl_SSL()
        {
            Uri initialUrl = new Uri(@"https://localhost/test/testFile.aspx?id=123");
            string expected = new Uri(@"https://localhost/test/testFile.aspx").ToString();

            IUriWriter writer = UriWriter.CreateEmptyFromUri(initialUrl);
            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void StaticInitializer_UseSSL_True()
        {
            Uri initialUrl = new Uri(@"https://localhost/test/testFile.aspx?id=123");
            string expected = null;

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected, writer.UseHttps);
        }

        [Test]
        public void StaticInitializer_UseSSL_False()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?id=123");
            string expected = null;

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected, writer.UseHttps);
        }

        [Test]
        public void StaticInitializer_Port()
        {
            Uri initialUrl = new Uri(@"http://localhost:1010/test/testFile.aspx?id=123");
            int expected = 1010;

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected, writer.GetUriBuilder().Port);
        }
        #endregion

        #region SetValues
        [Test]
        public void SetValues_FromEmpty()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=10&b=False").ToString();

            IQueryStringToken<int> a = new ValueTypeToken<int>("a");
            IQueryStringToken<bool> b = new ValueTypeToken<bool>("b");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, 10);
            writer.SetToken(b, false);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValues_FromInitial()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?c=Hans");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=10&b=False&c=Hans").ToString();

            IQueryStringToken<int> a = new ValueTypeToken<int>("a");
            IQueryStringToken<bool> b = new ValueTypeToken<bool>("b");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, 10);
            writer.SetToken(b, false);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValues_With_SortDisabled()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?b=False&a=10").ToString();

            IQueryStringToken<int> a = new ValueTypeToken<int>("a");
            IQueryStringToken<bool> b = new ValueTypeToken<bool>("b");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(b, false);
            writer.SetToken(a, 10);

            writer.SortTokens = false;

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void RenderEmptyValuesTokens_False()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?b=False").ToString();

            IQueryStringToken<string> a = new StringToken("a") { RenderIfEmptyValue = false };
            IQueryStringToken<bool> b = new ValueTypeToken<bool>("b");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(b, false);
            writer.SetToken(a, "");

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }
        #endregion

        #region Properties
        [Test]
        public void Fragment_LoadFromUrl()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?a=dummy#bookmarkName");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual("bookmarkName", writer.Fragment); //verifies that the Fragment gets initialized
        }

        [Test]
        public void Fragment_LoadFromUrl_Null()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?a=dummy");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.IsNull(writer.Fragment);
        }

        [Test]
        public void Fragment_LoadFromUrl_SpecialChars()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?a=dummy");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.Fragment = "<æ>";

            //no encoding is performed
            Assert.AreEqual("http://localhost/test/testFile.aspx?a=dummy#<æ>", writer.GenerateQueryAsString());
        }
        #endregion

        #region Count
        [Test]
        public void CountTokens_WithInitialUrl()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?a=10&b=False");
            int expected = 2;

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            Assert.AreEqual(expected, writer.CountTokens());
        }

        [Test]
        public void CountTokens_WithInitialUrlAndNewTokens()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx?a=10&b=False");
            int expected = 3;

            IQueryStringToken<bool> c = new ValueTypeToken<bool>("c");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(c, true);
            Assert.AreEqual(expected, writer.CountTokens());
        }

        [Test]
        public void CountTokens_FromEmpty()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            int expected = 0;
            IQueryStringToken<bool> a = new ValueTypeToken<bool>("a");

            IUriWriter writer = UriWriter.CreateEmptyFromUri(initialUrl);
            Assert.AreEqual(expected, writer.CountTokens());

            writer.SetToken(a, true);
            expected = 1;
            Assert.AreEqual(expected, writer.CountTokens());

            writer.SetToken(a, true); //try to add again to verify that count remains unchanged.
            Assert.AreEqual(expected, writer.CountTokens());

        }
        #endregion

        [Test]
        public void GetUriBuilderTest()
        {
            Uri initialUrl = new Uri(@"https://localhost/test/testFile.aspx?id=123#sec10");
            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);

            UriBuilder builder = writer.GetUriBuilder();

            Assert.AreEqual("#sec10", builder.Fragment);
            Assert.AreEqual("?id=123", builder.Query);
            Assert.AreEqual("https", builder.Scheme);
            Assert.AreEqual("test/testFile.aspx", builder.Path);
            Assert.AreEqual("localhost", builder.Host);
        }

        [TestCase("page2.aspx", Result = "http://test.com/pages/page2.aspx")]
        [TestCase("page2aspx", ExpectedException = typeof(ArgumentException))]
        //[TestCase("..", ExpectedException = typeof(ArgumentException))]
        [TestCase(null, Result = "http://test.com/pages/")]
        [TestCase("", Result = "http://test.com/pages/")]
        public string SetFileNameTest(string fileName)
        {
            IUriWriter writer = UriWriter.CreateFromUri("http://test.com/pages/page1.aspx")
                .SetFileName(fileName);

            return writer.GenerateQueryAsString();
        }

        [TestCase("pages", Result = "http://test.com/pages/page1.aspx")]
        [TestCase("~", Result = "http://test.com/page1.aspx")]
        //[TestCase("..", ExpectedException = typeof(ArgumentException))]
        public string DirectoryAddTest(string dir)
        {
            IUriWriter writer = UriWriter.CreateFromUri("http://test.com/page1.aspx")
                .DirectoryAdd(dir);

            return writer.GenerateQueryAsString();
        }

        [Test]
        public void DirectoryFragmentsOnInitializeTest()
        {
            IUriWriter writer = UriWriter.CreateFromUri("http://test.com/pages/temp/default.aspx");

            var directoryFragments = writer.GetType()
                .GetField("_directoryFragments", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(writer) as IList<string>;

            Assert.AreEqual("pages", directoryFragments[0]);
            Assert.AreEqual("temp", directoryFragments[1]);
        }


        [TestCase("http://test.com/page1.aspx", 10, Result="http://test.com/page1.aspx")]
        [TestCase("http://test.com/dir1/dir2/page1.aspx", 2, Result = "http://test.com/page1.aspx")]
        [TestCase("http://test.com/dir1/dir2/page1.aspx", 0, Result = "http://test.com/dir1/dir2/page1.aspx")]
        [TestCase("http://test.com/dir1/dir2/page1.aspx", -1, ExpectedException = typeof(ArgumentException))]
        public string DirectoryBackTest(string initialUri, int numberOfDirectoriesToGoBack)
        {
            IUriWriter writer = UriWriter.CreateFromUri(initialUri)
                .DirectoryBack(numberOfDirectoriesToGoBack);

            return writer.GenerateQueryAsString();
        }


        [Test]
        public void DirectoryTest_CombinedManipulation()
        {
            IUriWriter writer = UriWriter.CreateFromUri("http://test.com/pages/temp/page1.aspx")
                .DirectoryBack(1)
                .DirectoryAdd("newDir")
                .SetFileName(null);

            Assert.AreEqual("http://test.com/pages/newDir/", writer.GenerateQueryAsString());
        }

        [Test]
        public void DirectoryTest_CombinedManipulation2()
        {
            IUriWriter writer = UriWriter.CreateFromUri("http://test.com/page1.aspx")
                .DirectoryAdd("pages")
                .DirectoryAdd("temp");

            Assert.AreEqual("http://test.com/pages/temp/page1.aspx", writer.GenerateQueryAsString());
        }


        #region Primitive values
        [Test]
        public void SetValue_Boolean()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=True").ToString();

            IQueryStringToken<bool> a = new ValueTypeToken<bool>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, true);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_Int()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=999").ToString();

            IQueryStringToken<int> a = new ValueTypeToken<int>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, 999);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_IntNegative()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=-999").ToString();

            IQueryStringToken<int> a = new ValueTypeToken<int>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, -999);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_Guid()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=662ac7a2-e549-47da-90ef-851e4da17546").ToString();

            IQueryStringToken<Guid> a = new ValueTypeToken<Guid>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, new Guid("662ac7a2-e549-47da-90ef-851e4da17546"));

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_Double()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = "http://localhost/test/testFile.aspx?a=123%2c456";

            IQueryStringToken<double> a = new ValueTypeToken<double>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, 123.456);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_TimeSpan()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = @"http://localhost/test/testFile.aspx?a=05%3a04%3a03";

            IQueryStringToken<TimeSpan> a = new ValueTypeToken<TimeSpan>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, new TimeSpan(5, 4, 3));

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_DateTime_DateOnly()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = @"http://localhost/test/testFile.aspx?a=26-02-2010+00%3a00%3a00";

            IQueryStringToken<DateTime> a = new ValueTypeToken<DateTime>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, new DateTime(2010, 2, 26));

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_DateTime_Full()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = @"http://localhost/test/testFile.aspx?a=26-02-2010+14%3a13%3a12";

            IQueryStringToken<DateTime> a = new ValueTypeToken<DateTime>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, new DateTime(2010, 2, 26, 14, 13, 12));

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }


        [Test]
        public void SetValue_Enum()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=2").ToString();

            IQueryStringToken<TestEnum> a = new ValueTypeToken<TestEnum>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, TestEnum.EnumValue2);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }

        [Test]
        public void SetValue_EnumFlags()
        {
            Uri initialUrl = new Uri(@"http://localhost/test/testFile.aspx");
            string expected = new Uri(@"http://localhost/test/testFile.aspx?a=68").ToString();

            IQueryStringToken<TestFlagEnum> a = new ValueTypeToken<TestFlagEnum>("a");

            IUriWriter writer = UriWriter.CreateFromUri(initialUrl);
            writer.SetToken(a, TestFlagEnum.EnumValue2 | TestFlagEnum.EnumValue6);

            Assert.AreEqual(expected, writer.GenerateQueryAsString());
        }
        #endregion
    }
}
